#include <pcap.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <linux/ip.h>
#include <netinet/ip6.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_ether.h>
#include <time.h>

#include "log.h"
#include "pcap_process.h"
#include "mem.h"
#include "core_data.h"
#include "sql.h"

#define LINK_OFFSET 14 /* DLT_EN10_MB */

extern unsigned long raw_packet_count;
extern unsigned long ipv4_packet_count;

#define TIME_INTERVAL 5
#define TIME_TABLE_INTERVAL 3600
static unsigned long time1 = 0;
static unsigned long time2 = 0;
static unsigned long table_time1 = 0; 
static unsigned long table_time2 = 0; 
static char table_name[128] = {'\0'};

static int table_id = 0;

static void do_processing(const struct iphdr *iph, unsigned short ipsize, unsigned short rawsize)
{
	log_info("begin do_process.");

	int res;
	table_time2 = time(NULL);
	if (table_time2 >= table_time1 + TIME_TABLE_INTERVAL) {
		char cmd[1024] = {'\0'};

		sprintf(table_name, "pcapInfo%lu", table_time2);
		sprintf(cmd, "create table %s(ipv4PacketsCount bigint, rawPacketsCount bigint)", table_name);
		res = sql_query(cmd);
		if (res) {
			log_print("sql_query error: %d, %s\n", sql_errorno(), sql_error());
		}

		sprintf(cmd, "insert into pcapTableInfo values(%d, '%s')", table_id, table_name);
		table_id++;
		res = sql_query(cmd);
		if (res) {
			log_print("sql_query error: %d, %s\n", sql_errorno(), sql_error());
		}

		table_time1 = table_time2;
	}

	/* only handle with ipv4 */
	if (iph->version == 4 && ipsize >= sizeof(struct iphdr)) {
		ipv4_packet_count++;
		/* Hope with SMP's help, it won't take too much time... */	
		void *data = s_malloc(ipsize);
		if (data == NULL) {
			log_error("No enough mem.");
			return;
		}

		memcpy(data, iph, ipsize);

		struct core_data_queue *core_data_queue = NULL;
		core_data_queue = core_conf.core_data_queue;
		if (core_data_queue == NULL) {
			log_error("core_data_queue is NULL.");
			s_free(data);
			return;
		}

		add_core_data(core_data_queue, data, ipsize, rawsize);

		time2 = time(NULL);	
		if (time2 >= time1 + TIME_INTERVAL) {
			char cmd[1024] = {'\0'};
			sprintf(cmd, "insert into %s values(%lu, %lu)", table_name, ipv4_packet_count, raw_packet_count);
			//printf("%s\n", cmd);
			res = sql_query(cmd);
			if (res) {
				log_print("sql_query error: %d, %s\n", sql_errorno(), sql_error());
			}

			time1 = time2;
		}

	}

}

void pcap_task_packet_process(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
	log_info("begin pcap_task_packet_process.");

	const struct ethhdr *ethernet = (struct ethhdr *)packet;
	struct iphdr *iph = (struct iphdr *)&packet[sizeof(struct ethhdr)];

	raw_packet_count++;

	unsigned short type = ethernet->h_proto;

	if (core_conf.pcap_datalink_type == DLT_EN10MB 
			&& (type == htons(ETH_P_IP)) 
			&& header->caplen >= sizeof(struct ethhdr)) {
		if (header->caplen < header->len) {
			static unsigned char cap_warning_used = 0;
			if (cap_warning_used == 0) {
				log_warning("packet capture size is smaller than pcasket size.");
				cap_warning_used = 1;
			}
		}

		if (header->len >= (sizeof(struct ethhdr) + sizeof(struct iphdr))) {
			do_processing(iph, header->len - sizeof(struct ethhdr), header->len);
		}
	}
		
	return;
}
